/********************************************************************
	Author:		Kanrong Shan
*********************************************************************/

#pragma once

// 
// This structs acts as a smart pointer for IUnknown pointers
// making sure to call AddRef() and Release() as needed.
//
template<typename T>
struct ComPtr
{
public:

	ComPtr(T* lComPtr = NULL) : m_ComPtr(lComPtr)
	{
		#if _MSC_VER >= 1600
		static_assert(std::tr1::is_base_of<IUnknown, T>::value, "T needs to be IUnknown based");
		#endif
		if (m_ComPtr)
		{
			m_ComPtr->AddRef();
		}
	}

	ComPtr(const ComPtr<T>& lComPtrObj)
	{
		#if _MSC_VER >= 1600
		static_assert(std::tr1::is_base_of<IUnknown, T>::value, "T needs to be IUnknown based");
		#endif
		m_ComPtr = lComPtrObj.m_ComPtr;

		if (m_ComPtr)
		{
			m_ComPtr->AddRef();
		}
	}

	#if _MSC_VER >= 1600
	ComPtr(ComPtr<T>&& lComPtrObj)
	{
		m_ComPtr = lComPtrObj.m_ComPtr;        
		lComPtrObj.m_ComPtr = NULL;
	}    
	#endif

	T* operator=(T* lComPtr)
	{
		if (m_ComPtr)
		{
			m_ComPtr->Release();
		}

		m_ComPtr = lComPtr;

		if (m_ComPtr)
		{
			m_ComPtr->AddRef();
		}

		return m_ComPtr;
	}

	T* operator=(const ComPtr<T>& lComPtrObj)
	{
		if (m_ComPtr)
		{
			m_ComPtr->Release();
		}

		m_ComPtr = lComPtrObj.m_ComPtr;

		if (m_ComPtr)
		{
			m_ComPtr->AddRef();
		}

		return m_ComPtr;
	}

	~ComPtr()
	{
		if (m_ComPtr)
		{
			m_ComPtr->Release();
			m_ComPtr = NULL;
		}
	}

	operator T*() const
	{
		return m_ComPtr;
	}

	T* GetInterface() const
	{
		return m_ComPtr;
	}

	T& operator*() const
	{
		return *m_ComPtr;
	}

	T** operator&()
	{
		//The assert on operator& usually indicates a bug. Could be a potential memory leak.
		// If this really what is needed, however, use GetInterface() explicitly.
		assert(NULL == m_ComPtr);
		return &m_ComPtr;
	}

	T* operator->() const
	{
		return m_ComPtr;
	}

	bool operator!() const
	{    
		return (NULL == m_ComPtr);
	}

	bool operator<(T* lComPtr) const
	{
		return m_ComPtr < lComPtr;
	}

	bool operator!=(T* lComPtr) const
	{
		return !operator==(lComPtr);
	}

	bool operator==(T* lComPtr) const
	{
		return m_ComPtr == lComPtr;
	}

	template <typename I>
	HRESULT QueryInterface(I **interfacePtr)
	{
		return m_ComPtr->QueryInterface(IID_PPV_ARGS(interfacePtr));
	}

protected:
	// The internal interface pointer
	T* m_ComPtr;
};
